#include <UnitTest++.h>
#include "../getvoltages.h"
#include "../job.h"
#include <iostream>
#include <string>
#include "../option.h"
#include "../MyMap.h"
#include "fixturesInputOutput.h"
#include <cmath>  // not math.h which in c library

using namespace std;

TEST(JobClassPassAlways)
{
    CHECK(true);
}

TEST_FIXTURE(InputOptionFixture, JobTestInputOptionFixture)
{
	CHECK_EQUAL(22, temp);
	CHECK_EQUAL(0, opt.L["messagelevel"]);
	CHECK_EQUAL(opt.NIDAQConstants["terminalmode"][3], opt.L["terminalmode"]);
//	cout << opt.toString() << endl;
}

TEST_FIXTURE(InputOptionFixture, InputJobReadOneSample)
{
	/* No need to test subclasses directly as Job interface doesn't expose the
	derived classes functions
	*/
	persistJobHolder persistJobs;
	opt.L["samples"]=1;			//for inputJob one sample
	{
		InputJob* jobptr =0;
		jobptr = new InputJob("single sample",persistJobs,opt);
		CHECK(jobptr != 0);
		CHECK(jobptr->getTaskHandle() !=0);
		CHECK_EQUAL(1, jobptr->getPostTrigSize());
		jobptr->readData();
		CHECK_CLOSE(0, jobptr->getPostTrigData()[0],10); // +/- 10 volts
//		cout << "Show one point: " << jobptr->getPostTrigData()[0] << endl;
		delete jobptr;
	}
}

TEST_FIXTURE(InputOptionFixture, BufferedInputJobRead50Points)
{
	/* not need to test subclasses directly as Job interface doesn't expose the
	derived classes functions
	*/
	persistJobHolder persistJobs;
	{
//		std::cout << "starting BufferedInputJobRead50Points" << std::endl;
		BufferedInputJob* jobptr =0;
		jobptr = new BufferedInputJob("single sample",persistJobs,opt);
		CHECK(jobptr != 0);
		CHECK(jobptr->getTaskHandle() !=0);
        CHECK_EQUAL(50, opt.L["samples"]);
		CHECK_EQUAL(opt.L["samples"], jobptr->getPostTrigSize());
		jobptr->readData();
//		std::cout << "now getPostTrigData() is " << jobptr->getPostTrigData() << std::endl;
		CHECK_CLOSE(0, jobptr->getPostTrigData()[0],10); // +/- 10 volts
//		cout << opt.L["samples"] << " points" << endl;
		for(int i=0; i <50; i++)
		{
//			cout << i << "\t" << jobptr->getPostTrigData()[i] << endl;
			CHECK_CLOSE(0, jobptr->getPostTrigData()[i],10); // +/- 10 volts
		}
		delete jobptr;
//        std::cout << "exiting BufferedInputJobRead50Points" << std::endl;
	}
}

TEST_FIXTURE(InputOptionFixture, ReadOneImmediateFromPersistingWrite_Temp)
{
	// call structure directly or form structure of structures (in header?)?
	struct InputOptionFixture In;

	persistJobHolder persistJobs;
	In.opt.L["samples"] = 1;			//for inputJob one sample
	In.opt.L["numchannels"] = 2;			//for inputJob one sample
	In.opt.S["channels"] += ", dev1/ai1";
	InputJob inJob("single sample", persistJobs, In.opt);
	inJob.readData();
	CHECK_EQUAL(In.opt.L["samples"] * In.opt.L["numchannels"], inJob.getPostTrigSize());

	// Assuming connection across two nearly equal resistors
	double total = inJob.getPostTrigData()[0];
	double half =  inJob.getPostTrigData()[1];
	double percent = 2.0/100.0;
	CHECK_CLOSE(total, half * 2 , abs(percent * total) +1e-2);

	//~ cout << "SampleArraySize is :" << inJob.getSampleArraySize() << endl;
	//~ cout << "First Channel: " << inJob.getPostTrigData()[0] << endl;
	//~ cout << "Second Channel: " << inJob.getPostTrigData()[1] << endl;
}

TEST(WriteAndReadOneSampleImmeditate)
{
	struct InputOptionFixture In;
	struct OutputFixture Out;
	persistJobHolder persistJobs;
	double voltValue = 1.34;
	double zeroVolts = 0.00;
	Out.opt.DArray["waveform"][0] = voltValue;
	{
		OutputJob* jobptr = CreateOutputJob(persistJobs, Out.opt);
		CHECK(jobptr->getTaskHandle() !=0);
		OutputWaveformDispatch(jobptr, persistJobs, Out.opt);
	}
	In.opt.L["samples"] = 1;			//for inputJob one sample
	InputJob inJob("single sample", persistJobs, In.opt);
	inJob.readData();
	double readValue = inJob.getPostTrigData()[0];
	CHECK_CLOSE(voltValue, readValue , 1e-2);
	{
		Out.opt.DArray["waveform"][0] = zeroVolts;
		OutputJob* jobptr = CreateOutputJob(persistJobs, Out.opt);
		OutputWaveformDispatch(jobptr, persistJobs, Out.opt);
	}
}

TEST_FIXTURE(IOFixture,WriteThenReadOneSampleImmediate)
{
	double voltValue = 1.34;
	Out.opt.DArray["waveform"][0] = voltValue;
	WriteImmediate();

	In.opt.L["samples"] = 1;			//for inputJob one sample
	InputJob* pInJob = ReadImmediate("Single Sample");
	double readValue = pInJob->getPostTrigData()[0];
	CHECK_CLOSE(voltValue, readValue , 1e-2);

	Out.opt.DArray["waveform"][0] = zeroVolts;			// should be in dtor
	WriteImmediate();

}

TEST(WriteAndReadTwoSamplesImmeditate)
{
	struct InputOptionFixture In;
	struct OutputFixture Out;
	persistJobHolder persistJobs;
	double voltValue0 = 3.0;
	double voltValue1= 4.0;
	double zeroVolts = 0.00;
	Out.opt.DArray["waveform"][0] = voltValue0;
	Out.opt.DArray["waveform"][1] = voltValue1;
	Out.opt.L["numchannels"]=2;			//for two channels
	Out.opt.S["channels"] += ", dev1/ao1";
//	cout << Out.opt.toString() << endl;
	{
		OutputJob* jobptr = CreateOutputJob(persistJobs, Out.opt);
		CHECK(jobptr->getTaskHandle() !=0);
		OutputWaveformDispatch(jobptr, persistJobs, Out.opt);
	}
	In.opt.L["samples"] = 1;			//for inputJob one sample
	In.opt.L["numchannels"]=2;			//for two channels
	In.opt.S["channels"] += ", dev1/ai3";
//	cout << In.opt.toString() << endl;

	InputJob inJob("two channel 1 sample each", persistJobs, In.opt);
//	cout << "SampleArraySize = " << inJob.getSampleArraySize() << endl;
	inJob.startTask(inJob.getTaskHandle());
	inJob.readData();

	double *arr = inJob.getPostTrigData();
	double readValue0 = arr[0];
	double readValue1 = arr[1];
//	cout << arr[0] << " ------" << arr[1] << endl;
	CHECK_CLOSE(voltValue0, readValue0, 1e-2);
	CHECK_CLOSE(voltValue1, readValue1, 1e-2);
	{
		Out.opt.DArray["waveform"][0] = zeroVolts;
		Out.opt.DArray["waveform"][1] = zeroVolts;
		OutputJob* jobptr = CreateOutputJob(persistJobs, Out.opt);
		OutputWaveformDispatch(jobptr, persistJobs, Out.opt);
	}
}

TEST(TwoOutputTasksActiveAtSampleTime)
{
	struct OutputFixture Out;
	persistJobHolder persistJobs;
	double voltValue0 = 3.0;
	Out.opt.DArray["waveform"][0] = voltValue0;
	{
		Out.opt.S["channels"] = "dev1/ao0";
		OutputJob* jobptr1 = CreateOutputJob(persistJobs, Out.opt);
		CHECK(jobptr1->getTaskHandle() !=0);
		Out.opt.S["channels"] = "dev1/ao1";
		OutputJob* jobptr2 = CreateOutputJob(persistJobs, Out.opt);
		CHECK(jobptr2->getTaskHandle() !=0);
		delete jobptr1;
		delete jobptr2;
	}
}
TEST(TwoBuffedOutputTasksActiveAtSampleTime)
{
	struct OutputFixture Out;
	persistJobHolder persistJobs;
	double voltValue0 = 3.0;
	Out.opt.DArray["waveform"][0] = voltValue0;
	Out.opt.S["samples"] = 10;
	{
		Out.opt.S["channels"] = "dev1/ao0";
		OutputJob* jobptr1 = new BufferedOutputJob("Same Name", persistJobs, Out.opt);
		CHECK(jobptr1->getTaskHandle() !=0);
		Out.opt.S["channels"] = "dev1/ao1";
		OutputJob* jobptr2 = new BufferedOutputJob("Same Name", persistJobs, Out.opt);
		CHECK(jobptr2->getTaskHandle() !=0);
		delete jobptr1;
		delete jobptr2;
	}
}
TEST(TwoInputTasksActiveAtSampleTime)
{
	struct InputOptionFixture In;
	persistJobHolder persistJobs;
	In.opt.S["samples"] = 1;
	{
		In.opt.S["channels"] = "dev1/ai0";
		InputJob* jobptr1 = new InputJob("Same Name", persistJobs, In.opt);
		CHECK(jobptr1->getTaskHandle() !=0);
		In.opt.S["channels"] = "dev1/ai1";
		InputJob* jobptr2 = new InputJob("Same Name", persistJobs, In.opt);
		CHECK(jobptr2->getTaskHandle() !=0);
		delete jobptr1;
		delete jobptr2;
	}
}

//~ TEST_FIXTURE(IOFixture, GetPersistentTaskList)
//~ {
	//~ // notfinished so next line is intentional error
	//~ CHECK_EQUAL("fix_or_delete_this_test","Intentional Fail");

	//~ Out.opt.DArray["waveform"][0] = 00.0;
	//~ Out.opt.DArray["waveform"][1] = 0.00;
	//~ Out.opt.L["numchannels"] = 2;			//for two channels
	//~ Out.opt.S["channels"] += ", dev1/ao1";
	//~ {
		//~ OutputJob* jobptr = CreateOutputJob(persistJobs, Out.opt);
		//~ CHECK(jobptr->getTaskHandle() !=0);
		//~ OutputWaveformDispatch(jobptr, persistJobs, Out.opt);
	//~ }

	//~ map<TaskHandle,JobState> JL = persistJobs.getJobList();
	//~ typedef map<TaskHandle, JobState>::iterator jobList_iter;
	//~ bool32 taskIsDone = false;
	//~ char chanName[100];
	//~ for(jobList_iter iJL = JL.begin(); iJL != JL.end(); iJL++) {
		//~ cout << "TaskHandle = " << iJL->first << endl;
		//~ cout << "from job pointer get channels = " <<  iJL->second.pJob->getoptS("channels") << endl;
		//~ DAQmxGetTaskName( iJL->first, chanName,100);
		//~ DAQmxGetTaskComplete(iJL->first, &taskIsDone);
		//~ cout << "Task Name is: " << chanName << " -- status: "<< taskIsDone<<endl;
	//~ }
//~ }

